/*
 *
 *   Copyright (C) 2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <stdio.h>
#include <urlglib/msg_server.h>
#include <urlglib/msg_client.h>

#ifdef _WIN32
// Windowns version --------------------------------------------------
#include <winsock.h>

static void msg_server_delay ()
{
	Sleep (100);    // 0.1 seconds
}

MsgServer* msg_server_new ()
{
	SOCKET  fd;
	struct sockaddr_in saddr;
	MsgServer* msgs;

	fd = socket (AF_INET, SOCK_STREAM, 0);
	if (fd == INVALID_SOCKET)
		return NULL;

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons (URLGFE_SOCKET_PORT);
	saddr.sin_addr.S_un.S_addr = inet_addr ("127.0.0.1");

	if (bind (fd, (struct sockaddr *)&saddr, sizeof (saddr)) == SOCKET_ERROR) {
		closesocket (fd);
		return NULL;
	}
	listen (fd, 5);

	msgs = g_malloc (sizeof (MsgServer));
	msgs->fd = fd;
	msgs->reference_count = 1;
	msgs->addr = saddr;
	msgs->url_list = NULL;
	msgs->active = FALSE;
	g_static_mutex_init (&msgs->mutex);

	return msgs;
}

void  msg_server_finalize (MsgServer* msgs)
{
	if (msgs->fd != INVALID_SOCKET)
		closesocket (msgs->fd);
	g_static_mutex_free (&msgs->mutex);
}

gboolean   msg_server_has_message (MsgServer* msgs)
{
	int bytes_received;
	char data[1];
	unsigned int packetlength;
	u_long arg;
	int error;

	arg = 1;
	ioctlsocket (msgs->fd, FIONBIO, &arg); /* set to non-blocking mode */

	packetlength = 1;
	bytes_received = recvfrom (msgs->fd, (void*) data, packetlength,
	                           MSG_PEEK, NULL, NULL);

	error = WSAGetLastError ();

	arg = 0;
	ioctlsocket (msgs->fd, FIONBIO, &arg); /* set blocking mode */

	if (bytes_received == SOCKET_ERROR) {
		if (WSAEMSGSIZE != error) {
			return FALSE;
		}
		/* else, the buffer was not big enough, which is fine since we
		   just want to see if a packet is there..*/
	}

	if (bytes_received)
		return TRUE;

	return FALSE;
}
#else
// Unix version ------------------------------------------------------
#include <sys/socket.h>    // socket api
#include <sys/un.h>        // struct sockaddr_un
#include <sys/time.h>      // struct timeval
#include <unistd.h>        // usleep ()

#define INVALID_SOCKET  (-1)
#define closesocket     close

static void msg_server_delay ()
{
	usleep (100000);    // 0.1 seconds
}

MsgServer* msg_server_new ()
{
	int  fd;
	struct sockaddr_un saddr;
	MsgServer* msgs;

	fd = socket (AF_UNIX, SOCK_STREAM, 0);
	if (fd == -1)
		return NULL;

	saddr.sun_family = AF_UNIX;
	sprintf (saddr.sun_path, "%s/%s_%s",
	         g_get_tmp_dir (), URLGFE_SOCKET_NAME, g_get_user_name ());
	ug_unlink (saddr.sun_path);
	if (bind (fd, (struct sockaddr *)&saddr, sizeof (saddr)) == -1) {
		close (fd);
		return NULL;
	}
	listen (fd, 5);

	msgs = g_malloc (sizeof (MsgServer));
	msgs->fd = fd;
	msgs->reference_count = 1;
	msgs->filename = g_strdup (saddr.sun_path);
	msgs->url_list = NULL;
	msgs->active = FALSE;
	g_static_mutex_init (&msgs->mutex);

	return msgs;
}

void  msg_server_finalize (MsgServer* msgs)
{
	if (msgs->fd != -1)
		close (msgs->fd);

	if (msgs->filename) {
		ug_unlink (msgs->filename);
		g_free (msgs->filename);
	}
	g_static_mutex_free (&msgs->mutex);
}

static gboolean   msg_server_has_message (MsgServer* msgs)
{
	fd_set set;
	struct timeval tv;

	FD_ZERO (&set);
	FD_SET (msgs->fd, &set);
	tv.tv_sec=0;
	tv.tv_usec=0;

	if (select (msgs->fd+1, &set, NULL, NULL, &tv) > 0)
		return TRUE;
	return FALSE;
}

#endif


// common ------------------------------------------------------------

void  msg_server_ref (MsgServer* msgs)
{
	msgs->reference_count++;
}

void  msg_server_unref (MsgServer* msgs)
{
	if (--msgs->reference_count == 0)
		msg_server_finalize (msgs);
}

void  msg_server_lock (MsgServer* msgs)
{
	g_static_mutex_lock (&msgs->mutex);
}

void  msg_server_unlock (MsgServer* msgs)
{
	g_static_mutex_unlock (&msgs->mutex);
}

static gpointer  msg_server_run (MsgServer* msgs)
{
	SOCKET nfd;
	int addr_len = sizeof(msgs->addr);
	int len;
	struct MsgPacket packet;
	gchar* url_string;

	while (msgs->active) {
		// avoid some kind of attack...
		do {
			msg_server_lock (msgs);
			len = g_list_length (msgs->url_list);
			msg_server_unlock (msgs);
			msg_server_delay ();
		}
		while (len > 64);

		printf ("server accepting...\n");
		nfd = accept (msgs->fd, (struct sockaddr *) &msgs->addr, &addr_len);
		if (nfd == INVALID_SOCKET)
			break;
		len = recv (nfd, (char*) &packet, sizeof (packet), 0);
		printf ("recv len : %d\n", len);
		if (len == sizeof (packet) && packet.length <= 32768) {
			switch (packet.type) {
			case MESSAGE_ACK:
				packet.type = MESSAGE_ACK;
				packet.length = 0;
				send (nfd, (char*) &packet, sizeof (packet), 0);
				break;
			case MESSAGE_ADD:
				printf ("server add\n");
				url_string = g_malloc (packet.length + 1);
				url_string[packet.length] = 0;
				len = recv (nfd, url_string, packet.length, 0);
				if (len == packet.length) {
					msg_server_lock (msgs);
					msgs->url_list = g_list_append (msgs->url_list, url_string);
					msg_server_unlock (msgs);
				}
				break;
			}
		}
		closesocket (nfd);
	}

	msg_server_unref (msgs);
	return NULL;
}

void  msg_server_activate (MsgServer* msgs)
{
	if (msgs->active == FALSE) {
		msgs->active = TRUE;
		msg_server_ref (msgs);
		g_thread_create ((GThreadFunc) msg_server_run, msgs, FALSE, NULL);
	}
}

void  msg_server_stop (MsgServer* msgs)
{
	msgs->active = FALSE;
	msg_client_ack ();
}

